#include <sys/types.h>         
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/stat.h> 
#include <pthread.h>
#include <fcntl.h>
#include <errno.h>
#include <stdbool.h>
typedef struct user{
    char name[21];
    char password[21];
   
}User;

typedef struct message{
    int type;
    User data;
    char warehouse[1024];//存储私聊时的信息
}Msg;


//客户端 socket
int clientFd;
//要下载文件的大小
int file_size=0;
//统计收到多少字节
int cnt_size=0;

//注册函数
void register_user(int clientFd);
//登陆函数
Msg login(int clientFd);
//群聊函数
void GroupChat(int clientFd,Msg msg);
//用于接收群聊信息的线程函数
void* thread_recv(void* n);
//用于接收文件的线程函数
void* thread_recvfile(void* n);
//私聊函数
void singel(int clientFd,Msg msg);
//方便群聊退出
void heap( Msg msg_charge);
// 显示聊天记录
void display_chatlog();
//指令判断
bool charge(char* arr,Msg msg);
//文件发送
void FileTransform(int fd);
//文件下载
void DownloadFile();
int main(){
    char buff[1024];
  
    fd_set fds ;
    //1
    clientFd = socket(AF_INET,SOCK_STREAM,0);
    int maxfd=clientFd;
    if(-1 == clientFd) printf("创建socketfd失败:%m\n"),exit(-1);
    printf("创建socketfd %m\n");

    //2 服务器的协议地址簇
    struct sockaddr_in sAddr={0};
    sAddr.sin_family = AF_INET;
    sAddr.sin_addr.s_addr = inet_addr("192.168.10.172");
    sAddr.sin_port = htons(9998);//使用1W左右的

    //3 连接服务器
    int r = connect(clientFd,(struct sockaddr*)&sAddr,sizeof sAddr);
    if(-1 == r) printf("连接服务器失败:%m\n"),close(clientFd),exit(-1);
    printf("连接服务器:%m\n");
    while(1){
        printf("1:注册，2：登录\n");
        int num;
        scanf("%d",&num);
        switch(num){
            case 1: register_user(clientFd) ; break;
            case 2: {
                Msg msg_charge=login(clientFd);
                heap(msg_charge);
            }
            break;
        }   
    }
    return 0;
}
void register_user(int clientFd){  
    Msg msg;
    msg.type=1;
    printf("请输入用户名和密码\n");
    User res;
    scanf("%s%s",res.name,res.password);
    msg.data=res;
    send(clientFd,&msg,sizeof(msg),0);
    Msg msg1={0};
    int r=recv(clientFd,&msg1,sizeof msg1,0);
    if(r>0){
        printf("%s\n",msg1.warehouse);  
    } 
    
}
Msg login(int clientFd){//登陆函数
    printf("请输入用户名和密码\n");
    User res; // 这个注册结构体是用来登录的
    scanf("%s%s", res.name, res.password);
    Msg msg; //发送一个结构体给服务器
    // 把数据放入轮船
    msg.type = 2;                     
    msg.data=res;
    send(clientFd,&msg,sizeof msg,0);     // 把登录信息发给服务器

    // 接受服务器返回的消息
    read(clientFd, &msg, sizeof(msg)); // 继续用msg结构体变量来接受返回消息
    if (666 == msg.type ){
        printf("登录成功！\n");
        //strcpy(loginUserName,res.name);//保存登录用户的名字到全局变量中，其他函数可能需要用到
        return msg;
    }else if (120 == msg.type){
        printf("密码错误\n");
        return msg;
    }else if(888 == msg.type ){
        printf("用户已在线\n");
        return msg;
    }
}

void GroupChat(int clientFd,Msg msg){//群聊
    msg.type=3;
    send(clientFd,&msg,sizeof msg,0);
    pthread_t pth; 
    pthread_create(&pth,NULL,thread_recv,(void*)1);
    
    while(1){ 
        if(-2==file_size){
            pthread_t pth; 
            pthread_create(&pth,NULL,thread_recv,(void*)1);
            file_size=-3;
        }
        char temp_arr[256];
        bzero(temp_arr,256);
        scanf("%s",temp_arr);
        bool chg=charge(temp_arr,msg);
        if(true == chg) continue;
        strcpy(msg.data.password,temp_arr);
        send(clientFd,&msg,sizeof msg,0);
    }
}
//用于接收群聊信息的线程函数
void* thread_recv(void* n){
    while(1){
        //pthread_mutex_lock(&clients_mutex);  // 上锁
        Msg msg1={0};
        int r=recv(clientFd,&msg1,sizeof msg1,0);
        //pthread_mutex_unlock(&clients_mutex);  // 解锁
        if(r>0){
                printf("%s\n",msg1.warehouse); 
       } 
       if(-1==file_size) return NULL;
    }

}

//用于接收文件的线程函数
void* thread_recvfile(void* n){
    file_size=-1;
    //cnt_size=0;
    while(1){
        Msg msg1={0};
        int r=recv(clientFd,&msg1,sizeof msg1,0);
        if(r>0){
            if(7 == msg1.type){
                if(strcmp(msg1.warehouse,"木有文件")==0){
                    printf("%s\n",msg1.warehouse);
                    return NULL;
                }
                printf("%s\n",msg1.warehouse);
            }else if(6 == msg1.type){//文件传输
                int fds=open(msg1.data.name,O_CREAT|O_WRONLY,0666);
                file_size=atoi(msg1.data.password);
                int r=write(fds,msg1.warehouse,sizeof msg1.warehouse);
                cnt_size+=r;
                printf("文件名：%s 进度：%d->%d\n",msg1.data.name,cnt_size,file_size);
                close(fds);
                if(file_size == cnt_size){
                    printf("下载完成\n");
                    return NULL;//结束线程
                } 
            }  
       } 
        
    }
}
//私聊函数
void singel(int clientFd,Msg msg){
    msg.type=4;
    pthread_t pth;
    pthread_create(&pth,NULL,thread_recv,(void*)1);
    while(1){
        if(-2==file_size){
            pthread_t pth; 
            pthread_create(&pth,NULL,thread_recv,(void*)1);
            file_size=-3;
        }
        printf("请输入收件人\n");
        char temp_arr2[256];
        bzero(temp_arr2,256);
        scanf("%s",temp_arr2);
        bool chg=charge(temp_arr2,msg);//指令判断
        if(true == chg) continue;
        strcpy(msg.data.password,temp_arr2);
        printf("请输入要发送的内容\n");
        bzero(temp_arr2,256);
        scanf("%s",temp_arr2);
        chg=charge(temp_arr2,msg);//指令判断
        if(true == chg) continue;
        strcpy(msg.warehouse,temp_arr2);
        send(clientFd,&msg,sizeof msg,0);  
    }
    
}
void heap( Msg msg_charge){
    if(666==msg_charge.type){//登陆成功之后才能选择功能
        printf("1:群聊，2：单聊，3:文件下载 4：退出\n");
         int num=0;
        scanf("%d",&num);
        switch(num){
            case 1: GroupChat(clientFd,msg_charge); break;
            case 2: singel(clientFd,msg_charge);    break;
            case 3: DownloadFile() ;                break;
            case 4:  exit(-1);                      break;
        }
    }
}
// 显示聊天记录
void display_chatlog() {
    FILE *logfile = fopen("groupchat.txt", "r");
    if (logfile != NULL) {
        char buffer[1024];
        while (fgets(buffer, sizeof(buffer), logfile) != NULL) {
            printf("%s\n", buffer);  // 打印聊天记录
        }
        fclose(logfile);
    } else {
        perror("Failed to open log file");
    }
}
//文件发送
void FileTransform(int fd){
    Msg msg1={0};
    //msg1.warehouse 存储文件的内容 msg1.data.name 存储文件名 msg1.data.password 存储文件大小
    msg1.type=5;
    //1.先发送文件名和文件大小
    printf("请输入文件名\n");
    scanf("%s",msg1.data.name);
    struct stat file_stat;
    if (stat(msg1.data.name, &file_stat) == 0) {
        // 如果stat函数执行成功，输出文件大小
        //printf("文件大小为 %ld 字节\n", file_stat.st_size);
        sprintf(msg1.data.password,"%ld",file_stat.st_size);
        send(fd,&msg1,sizeof msg1,0);
        long filesize=0;//统计发送的文件大小，发送完成结束循环
        //打开文件
        int fds=open(msg1.data.name,O_RDONLY);
        while(1){
            int r=read(fds,msg1.warehouse,sizeof msg1.warehouse);
            send(fd,&msg1,sizeof msg1,0);
            filesize+=r;
            if(filesize == file_stat.st_size){
                printf("传输完成\n");
                break;
            } 
        }
    } else {
        // 如果stat函数执行失败，检查并输出错误
        perror("stat函数失败:");
        int errno;
        if (errno == ENOENT){
            printf("文件不存在\n");
            printf("请检查文件名是否正确\n");
        }
    }
}
//指令判断
bool charge(char* arr,Msg msg){
    if(strcmp(arr,"QUIT")==0){
        msg.type=666;
        heap(msg);
        return true;
    }

    if(strcmp(arr,"FILE")==0){
        FileTransform(clientFd);
        return true;
    }
    if(strcmp(arr,"DOWNLOAD")==0){
        DownloadFile();
        return true;
    }
    return false;
}


void DownloadFile(){
    Msg msg1={0};
    msg1.type=6;//标识要下载文件
    //要区分接收存在的所有文件名 和 接收文件
    //线程里面区分
    pthread_t pth1;
    pthread_create(&pth1,NULL,thread_recvfile,(void*)1);
    memcpy(msg1.warehouse,"LOOK",sizeof("LOOK"));
    send(clientFd,&msg1,sizeof msg1,0);
    printf("请输入要下载的文件\n");
    scanf("%s",msg1.data.name);
    memcpy(msg1.warehouse,"kaishi",sizeof("kaishi"));
    send(clientFd,&msg1,sizeof msg1,0);
    printf("发送成功 %d\n",msg1.type);
    //等待线程结束
    pthread_join(pth1,NULL);
    file_size=-2;

}